home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 17
/
CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso
/
CUCD
/
Programming
/
DiceSource
/
lib
/
extra
/
wildcard.c
< prev
Wrap
C/C++ Source or Header
|
1997-09-09
|
8KB
|
377 lines
/*
* WILDCARD.C AmigaDos wildcard comparator
*
* WARNING: Uses *Lots* of stack
*
*
* (c)Copyright 1992-1997 Obvious Implementations Corp. Redistribution and
* use is allowed under the terms of the DICE-LICENSE FILE,
* DICE-LICENSE.TXT.
*/
#define _EXTRA_WILDCARD_C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <lib/misc.h>
#define SetWildStack _SetWildStack
#define CompWild _CompWild
#define ParseWild _ParseWild
#define FreeWild _FreeWild
/*
* Example: #(a|b|c)xx consts of:
*
* masternode(#), wn_Next = xx
* wn_Sub = (a|b|c)
*
* (a|b|c) wn_Next -> a, wn_Next = NULL, wn_Sib = b
* b, wn_Next = NULL, wn_Sib = c
* c, wn_Next = NULL, wn_Sib = NULL
*
* Etc...
*
* The most complexity occurs when dealing with '#' ... this could
* probably be made more efficient. Basically, we run through all
* possible repeats from N to none at all. For example, if the pattern
* is #?x then we try ????x ???x ??x ?x, and x. The number of repeats
* we try is currently STUPIDLY set to the length of the name remaining
* since that is the worst case.
*
* Note that this algorithm correctly handles extremely complex patterns
* like: #(a|b#?|c).o
*/
typedef struct WildNode {
struct WildNode *wn_Next; /* next wild node (append to current) */
struct WildNode *wn_Sib; /* sibling wild node (a|b|c|d) */
struct WildNode *wn_Sub; /* sub wildnode #(a|b|c) */
struct WildNode *wn_Cont; /* used at Compare time */
const char *wn_Pat; /* patterns not containing | or # */
short wn_PatLen; /* length of pattern */
short wn_Type; /* '|', '#', or 0 */
short wn_Repeat; /* # of repeats (for '#') */
} WildNode;
void SetWildStack(long); /* global */
WildNode *ParseWild(const char *, short);
int CompWild(const char *, WildNode *, WildNode *);
void FreeWild(WildNode *);
WildNode *ParseSubWild(const char *, short); /* local */
int extinpat(const char *, int);
int extonepat(const char *, int);
/*
* Parse the structure & run
*/
static char *BotStack;
WildNode *
ParseWild(pat, patlen)
const char *pat;
short patlen;
{
short i;
WildNode *wn = NULL;
WildNode **wnext = &wn;
if (patlen == 0)
return(NULL);
while (i = extinpat(pat, patlen)) {
*wnext = ParseSubWild(pat, i);
wnext = &(*wnext)->wn_Sib;
pat += i;
patlen -= i;
if (patlen && *pat == '|') {
++pat;
--patlen;
}
}
return(wn);
}
static WildNode *
ParseSubWild(pat, patlen)
const char *pat;
short patlen;
{
WildNode *wn;
short i;
if (patlen == 0)
return(NULL);
wn = calloc(sizeof(WildNode), 1);
for (i = 0; i < patlen; ++i) {
if (pat[i] == '#' || pat[i] == '(')
break;
if (pat[i] == '\'')
++i;
}
if (i) {
wn->wn_Pat = pat;
wn->wn_PatLen = i;
wn->wn_Next = ParseSubWild(pat + i, patlen - i);
} else if (*pat == '#') {
++pat;
--patlen;
i = extonepat(pat, patlen);
wn->wn_Sub = ParseWild(pat, i);
wn->wn_Type = '#';
wn->wn_Next = ParseSubWild(pat + i, patlen - i);
} else if (*pat == '(') {
i = extonepat(pat, patlen);
wn->wn_Sub = ParseWild(pat + 1, i - 2);
wn->wn_Type = 0;
wn->wn_Next = ParseSubWild(pat + i, patlen - i);
}
return(wn);
}
void
FreeWild(wn)
WildNode *wn;
{
if (wn) {
if (wn->wn_Sib)
FreeWild(wn->wn_Sib);
if (wn->wn_Next)
FreeWild(wn->wn_Next);
if (wn->wn_Sub)
FreeWild(wn->wn_Sub);
free(wn);
}
}
void
SetWildStack(n)
long n;
{
BotStack = (char *)&n - n;
}
/*
* CompWild(). Pretty straight forward except for '#'.
*
* The 'cont' field is a linked string of nodes that specify patterns
* that must be successfully compared after the current pattern, 'wn',
* compares successfully. When we are dealing with repeats of a
* pattern, such as #(a|b).o, then the 'cont' field is used to specify
* that (a|b) should be repeated wn_Repeat times.
*
* A further complication occurs with: #(a|b#?).o, where we have a
* nested repeat loop. In this case as 'cont' is scanned ONLY the
* top repeat loop is decremented by the 'cont' code. The inner loop
* will be decremented by the '#' code. Thus, when we skip to the
* next 'cont' we must skip any nodes with non-zero wn_Repeat
*
* I am not entirely certain that I've done it properly, the code is
* complex.
*/
CompWild(name, wn, cont)
const char *name;
WildNode *wn;
WildNode *cont;
{
{
char x;
if (&x < BotStack) {
errno = ESTACK;
return(-1);
}
}
top:
if (wn == NULL) {
if (cont) {
if (cont->wn_Repeat) {
if (--cont->wn_Repeat) {
wn = cont->wn_Sub;
cont = cont;
} else {
wn = cont->wn_Sub;
cont = cont->wn_Cont;
while (cont && cont->wn_Repeat)
cont = cont->wn_Cont;
}
goto top;
} else {
wn = cont;
cont = cont->wn_Cont;
while (cont && cont->wn_Repeat)
cont = cont->wn_Cont;
goto top;
/*return(CompWild(name, cont, cont->wn_Cont));*/
}
}
if (*name)
return(-1);
return(0);
}
/*printf("foo %08lx (%d)%.*s\n", wn, wn->wn_PatLen, wn->wn_PatLen, wn->wn_Pat);*/
if (wn->wn_PatLen) {
const char *pat = wn->wn_Pat;
const char *oname = name;
short patlen;
for (patlen = wn->wn_PatLen; patlen > 0; --patlen, ++pat) {
switch(*pat) {
case '%':
break;
case '?':
if (*name == 0)
patlen = 0; /* fail */
++name;
break;
case '\'':
++pat;
--patlen;
/* fall through */
default:
{
char c1 = *name;
char c2 = *pat;
if (c1 != c2) {
if (c1 >= 'a' && c1 <= 'z')
c1 += 'A' - 'a';
if (c2 >= 'a' && c2 <= 'z')
c2 += 'A' - 'a';
if (c1 != c2)
patlen = 0; /* fail */
}
}
++name;
break;
}
}
if (patlen < 0 || CompWild(name, wn->wn_Next, cont) < 0) {
if (wn->wn_Sib)
return(CompWild(oname, wn->wn_Sib, cont));
return(-1);
}
return(0);
}
{
WildNode *next = wn->wn_Next;
if (next) {
next->wn_Cont = cont;
cont = next;
}
if (wn->wn_Type == '#') {
short i = strlen(name);
while (i > 0) {
wn->wn_Cont = cont;
wn->wn_Repeat = i;
if (CompWild(name, NULL, wn) >= 0) {
wn->wn_Repeat = 0;
return(0);
}
--i;
}
wn->wn_Repeat = 0;
if (CompWild(name, NULL, cont) < 0) {
if (wn->wn_Sib) {
wn = wn->wn_Sib;
goto top;
}
return(-1);
}
return(0);
} else {
if (CompWild(name, wn->wn_Sub, cont) < 0) {
if (wn->wn_Sib == NULL)
return(-1);
wn = wn->wn_Sib;
goto top;
/*return(CompWild(name, wn->wn_Sib, cont));*/
}
return(0);
}
}
}
static int
extonepat(pat, patlen)
const char *pat;
int patlen;
{
if (patlen <= 0) {
printf("PATLEN BAD %d\n", patlen);
return(0);
}
switch(*pat) {
case '?':
case '%':
return(1);
case '#':
return(1 + extonepat(pat + 1, patlen - 1));
case '|':
puts("extone, unexpected '|'");
return(0);
case '(':
{
short pcnt = 0;
short i = 0;
while (patlen) {
++i;
if (*pat == '(')
++pcnt;
if (*pat == ')' && --pcnt == 0)
return(i);
++pat;
--patlen;
}
}
puts("unexpected EOF looking for ')'");
return(0);
case '\'':
if (patlen < 2) {
puts("extone, unexpected EOF at '");
return(0);
}
return(2);
default:
return(1);
}
/* not reached */
}
static int
extinpat(pat, patlen)
const char *pat;
int patlen;
{
int i;
short paren = 0;
for (i = 0; i < patlen; ++i) {
if (pat[i] == '(')
++paren;
if (pat[i] == ')' && --paren < 0)
return(i);
if (pat[i] == '|' && paren == 0)
return(i);
}
return(i);
}